#ifndef __SINK__
#define __SINK__

#include <iostream>
#include <cstring>
#include <string>
#include <memory>
#include <time.h>
#include <fstream>
#include <cassert>
#include <sstream>

#include "util.cpp"

// 日志落地模块类

namespace log_p
{
    // 落地基类
    class LogSink
    {
    public:
        // 回调指针，创建派生类对象后， 通过基类指针访问派生类
        using ptr = std::shared_ptr<LogSink>;
        // 设置虚函数，派生类进行定义
        virtual void log(const char *str, size_t leng) = 0;
    };

    // 日志落地方向
    // 1.标准输出
    class CoutSink : public LogSink
    {
    protected:
        void log(const char *str, size_t length)
        {
            std::cout.write(str, length);
        }
    };

    // 2.文件写入
    class FileSink : public LogSink
    {
    public:
        ~FileSink()
        {
            _ofs.close();
        }

        FileSink(const std::string &path) : _path(path)
        {
            if (log_p::UTIL::file::FileIsExit(path) == false)
                log_p::UTIL::file::CreateDirctory(log_p::UTIL::file::GetPath(path));
            // 如果打开地址打开失败，则断言
            //  std::cout << _path << std::endl;
            _ofs.open(_path, std::fstream::binary | std::fstream::app);
            if (_ofs.good())
                assert(1);
        }

    protected:
        void log(const char *str, size_t length) override
        {
            _ofs.write(str, length);
        }

    private:
        std::string _path;
        std::fstream _ofs; // 写入文件句柄
    };

    std::string log_file_by_size = ""; // 储存当前日志文件的相对路径

    // 3.滚动文件(通过文件大小 & 时间)
    class RollFileBySizeSink : public LogSink
    {
    public:
        ~RollFileBySizeSink()
        {
            _ofs.close();
        }

        // file_max用户提供文件最大值的设置; path 有则使用存在的日志文件，没有则根据path创建日志文件
        RollFileBySizeSink(size_t file_max, const std::string &path = log_file_by_size)
            : base_path(path), _file_size_max(file_max)
        {
            // 1. 检查所在目录是否存在，没有则创建
            if (log_p::UTIL::file::FileIsExit(base_path) == false)
            {
                log_p::UTIL::file::CreateDirctory(log_p::UTIL::file::GetPath(base_path));
                createFile();
            }
            struct stat st;
            stat(actual_path.c_str(), &st);
            _file_size_cur = st.st_size;

            // std::cout << "debug" << std::endl;
        }

    protected:
        void log(const char *str, size_t length) override
        {
            if (_file_size_cur + length >= _file_size_max || _ofs.good() == false)
                createFile();
            _file_size_cur += length;
            _ofs.write(str, length);
        }

    private:
        // 构建带有日期后缀的日志文件
        bool createFile()
        {
            _ofs.close();
            // 1.获取时间字符串
            struct tm t;
            time_t tmp = log_p::UTIL::TIME::GetTime();
            localtime_r(&tmp, &t);
            char time[32] = {0};
            strftime(time, 31, "%y%m%d%H", &t);
            actual_path = base_path;
            actual_path += "-";
            actual_path += std::string(time);
            actual_path += ".log";
            actual_path += "-";
            actual_path += std::to_string(filenum++);
            _ofs.open(actual_path, std::fstream::app);
            if (_ofs.is_open() == false)
                std::cout << "fail" << std::endl;
            assert(_ofs.good());
            log_file_by_size = actual_path;
            _file_size_cur = 0;
        }

    private:
        std::string base_path; // 日志文件相对路径名
        std::string actual_path;
        std::fstream _ofs; // 创建好，或者是打开好的日志文件
        size_t _file_size_max;
        size_t _file_size_cur;

        int filenum = 0;
    };

    enum class time_gap
    {
        MINTUE = 0,
        DAY,
        HOUR,
    };

    // 3.滚动文件(通过文件大小 & 时间)
    class RollFileByTimeSink : public LogSink
    {
    public:
        ~RollFileByTimeSink()
        {
            _ofs.close();
        }

        // file_max用户提供文件最大值的设置; path 有则使用存在的日志文件，没有则根据path创建日志文件
        RollFileByTimeSink(time_gap it, const std::string &path = log_file_by_size)
            : base_path(path), _time_gap(RollFileByTimeSink::to_timegap(it))
        {
            // 1. 检查所在目录是否存在，没有则创建
            if (log_p::UTIL::TIME::GetTime() / _time_gap != time_quantum)
            {
                log_p::UTIL::file::CreateDirctory(log_p::UTIL::file::GetPath(base_path));
                createFile();
            }
        }

    protected:
        void log(const char *str, size_t length) override
        {
            if (log_p::UTIL::TIME::GetTime() / _time_gap != time_quantum)
                createFile();
            _ofs.write(str, length);
        }

    private:
        // 构建带有日期后缀的日志文件
        bool createFile()
        {
            _ofs.close();
            // 1.获取时间字符串
            struct tm t;
            time_t tmp = log_p::UTIL::TIME::GetTime();
            time_quantum = tmp / _time_gap;
            localtime_r(&tmp, &t);
            char time[32] = {0};
            strftime(time, 31, "%y:%m:%d:%H:%M", &t);
            actual_path = base_path;
            actual_path += "-";
            actual_path += std::string(time);
            actual_path += ".log";
            actual_path += "-";
            actual_path += std::to_string(filenum++);
            _ofs.open(actual_path, std::fstream::app);
            assert(_ofs.good());
        }

        static size_t to_timegap(time_gap it)
        {
            switch (it)
            {
            case time_gap::DAY:
                return 24 * 3600;
            case time_gap::MINTUE:
                return 10;
            case time_gap::HOUR:
                return 3600;
            default:
                return 24 * 3600;
            }
        }

    private:
        std::string base_path; // 日志文件相对路径名
        std::string actual_path;
        std::fstream _ofs;       // 创建好，或者是打开好的日志文件
        size_t _time_gap;        // 用户设置的时间间隔
        size_t time_quantum = 0; // 上次时间段

        int filenum = 0; // 创建的日志文件数
    };

    // 功能：用于创建LogSink对象; 定义与表现分离
    // C++可变参数模板
    class LogSinkFactory
    {
    public:
        using ptr = std::shared_ptr<LogSinkFactory>;

        template <class T, class... Args>
        // 这里的T是将要被实例化的具体LogSink类型，而Args...表示传递给T构造函数的一系列参数。
        // 使用std::forward确保了参数的完美转发。
        static LogSink::ptr CreateLogSink(Args &&...args)
        {
            return std::make_shared<T>(std::forward<Args>(args)...);
        }
    };

}

#endif
